package com.tannuo.dolphinnotedemo.sdk.device.protocol;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import com.tannuo.dolphinnotedemo.sdk.device.CommandListener;
import com.tannuo.dolphinnotedemo.sdk.device.DeviceCanvas;
import com.tannuo.dolphinnotedemo.sdk.device.DeviceScreen;
import com.tannuo.dolphinnotedemo.sdk.device.IService;
import com.tannuo.dolphinnotedemo.sdk.device.MessageReceiveListener;
import com.tannuo.dolphinnotedemo.sdk.util.DataLog;
import com.tannuo.dolphinnotedemo.sdk.util.DataUtil;
import com.tannuo.dolphinnotedemo.sdk.util.Logger;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;


/**
 * Created by nick on 2016/4/24.
 *
 */
public class ProtocolHandler {

    private final String TAG = ProtocolHandler.class.getSimpleName();

    private IProtocol mIProtocol;
    private IService mWifiService;
    private DeviceCanvas mDeviceCanvas;
    private DeviceScreen mDeviceScreen;

    private CommandListener mCommandListener;

    private HandlerThread mProtocolThread;
    private ProtocolParseHandler mHandler;
    public static final int MESSAGE_PROTOCOL_PARSE = 1;

    public ProtocolHandler(IProtocol protocol, IService wifiService,
                           DeviceCanvas deviceCanvas, DeviceScreen deviceScreen, CommandListener commandListener) {
        if (protocol == null || wifiService == null || deviceCanvas == null || deviceScreen == null) {
            throw new IllegalArgumentException();
        }
        mIProtocol = protocol;
        mWifiService = wifiService;
        mDeviceCanvas = deviceCanvas;
        mDeviceScreen = deviceScreen;

        mCommandListener = commandListener;

        // 设置收到消息的监听
        mWifiService.setOnReceiverListener(new MessageReceiveListener() {
            @Override
            public void onReceive(byte[] data) {
                sendMessage(ProtocolHandler.MESSAGE_PROTOCOL_PARSE, data);
            }
        });

        // 启动处理线程
        start();
    }

    private void start() {
        mProtocolThread = new HandlerThread("protocol_handler_thread");
        mProtocolThread.start();
        mHandler = new ProtocolParseHandler(mProtocolThread.getLooper(), this);
    }

    public void sendMessage(int what, Object obj) {
        mHandler.obtainMessage(what, obj).sendToTarget();
    }

    public void sendMessage(Message msg) {
        mHandler.sendMessage(msg);
    }

    public void post(Runnable runnable) {
        mHandler.post(runnable);
    }

    public void postDelayed(Runnable runnable, long delayMS) {
        mHandler.postDelayed(runnable, delayMS);
    }

    public void stop() {
        mProtocolThread.quit();
        mHandler.stop();
        DataLog.getInstance().close();
    }

    private static class ProtocolParseHandler extends Handler {
        private String TAG = ProtocolParseHandler.class.getSimpleName();
        private WeakReference<ProtocolHandler> wrProtocolHandler;
        private Thread mParseThread;
        private boolean mIsParsing = true;

        public ProtocolParseHandler(Looper looper, ProtocolHandler protocolHandler) {
            super(looper);
            wrProtocolHandler = new WeakReference<>(protocolHandler);
            mParseThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (mIsParsing) {
                        byte[] data = getFromCache();
                        if (data.length > 0) {
                            parse(data);
                        }
                    }
                }
            });
            mParseThread.setName("protocol_parse_thread");
            mParseThread.start();
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_PROTOCOL_PARSE:
                    byte[] buffer = (byte[]) msg.obj;
                    if (null == buffer) {
                        return;
                    }
                    addToCache(buffer);
                    break;
                default:
                    break;
            }
        }

        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        List<byte[]> dataCache = new ArrayList<>();

        private void addToCache(byte[] buffer) {
            try {
                lock.lock();
                dataCache.add(buffer);
                Log.d(this.getClass().getSimpleName(), String.format("/++ after add size: %s ++/", (dataCache.size())));
                condition.signal();
            } finally {
                lock.unlock();
            }
        }

        private byte[] getFromCache() {
            try {
                lock.lock();
                byte[] data = new byte[0];
                if (dataCache.size() > 0) {
                    data = dataCache.remove(0);
                    Log.d(this.getClass().getSimpleName(), String.format("/--after remove size: %s --/", (dataCache.size())));
                } else {
                    try {
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return data;
            } finally {
                lock.unlock();
            }
        }

        private void parse(byte[] data) {
            if (null == data || data.length == 0 ||
                    wrProtocolHandler.get() == null) {
                return;
            }
            ProtocolHandler handler = wrProtocolHandler.get();
            IProtocol protocol = handler.mIProtocol;
            IService wifiService = handler.mWifiService;
            DeviceCanvas deviceCanvas = handler.mDeviceCanvas;
            DeviceScreen deviceScreen = handler.mDeviceScreen;
            CommandListener commandListener = handler.mCommandListener;
            IMessage message = protocol.decode(data);
            dispatchMessage(wifiService, deviceCanvas, deviceScreen, message, commandListener);
        }

        private void dispatchMessage(IService wifiService, DeviceCanvas deviceCanvas, DeviceScreen deviceScreen, IMessage message, CommandListener commandListener) {
            // 首先判断是否id一致
            if (String.valueOf(DataUtil.bytesToIntLittleEndian(message.getId())).equals(deviceScreen.getDeviceID())) {
                int feature = message.getFeatureInt();
                switch (feature) {
                    case ProtocolBase.STATUS_REQUEST_SNAPSHOT:
                        if (deviceCanvas != null) deviceCanvas.snapshot();
                        if (commandListener != null) {
                            commandListener.onCommand(ProtocolBase.STATUS_REQUEST_SNAPSHOT, null);
                        }
                        break;
                    case ProtocolBase.STATUS_REQUEST_START_RECORDING:
                        if (deviceCanvas != null) {
                            deviceCanvas.startRecording();
                        }
                        if (commandListener != null) {
                            // TODO: 2016/8/5 需要重新定义CommandListener的信息格式
                            commandListener.onCommand(ProtocolBase.STATUS_REQUEST_START_RECORDING, null);
                        }
                        break;
                    case ProtocolBase.STATUS_REQUEST_STOP_RECORDING:
                        if (deviceCanvas != null) {
                            deviceCanvas.stopRecording();
                        }
                        if (commandListener != null) {
                            // TODO: 2016/8/5 需要重新定义CommandListener的信息格式
                            commandListener.onCommand(ProtocolBase.STATUS_REQUEST_STOP_RECORDING, null);
                        }
                        break;
                    case ProtocolBase.ERROR_NONE:
                    case ProtocolBase.ERROR_HEADER:
                    case ProtocolBase.ERROR_DATA_FEATURE:
                    case ProtocolBase.ERROR_ID:
                    case ProtocolBase.ERROR_DATA_LENGTH:
                    case ProtocolBase.ERROR_DATA:
                    case ProtocolBase.ERROR_END:
                    case ProtocolBase.ERROR_CHECKSUM:
                        Logger.e(TAG, "The message's Format is error!!");
                        break;
                    case ProtocolBase.STATUS_POINT_DATA:
                        deviceCanvas.setPoints(message);
                        break;
                    case ProtocolBase.STATUS_RESPONSE_SCREEN:
                        deviceScreen.setDeviceInfo(message);
                        break;
                    case ProtocolBase.STATUS_BEAT:
                        wifiService.beatAlive();
                }
            } else if (message.getFeatureInt() == ProtocolBase.STATUS_POINT_DATA_FROM_REMOTE) {
                deviceCanvas.setPoints(message);
            } else {
                Logger.e(TAG, "Message's id does not equal current saved screen id");
            }
        }

        public void stop() {
            mIsParsing = false;
        }
    }
}
